#include "../inc/threadPool.h"
#include "../inc/transmitFile.h"
#include "../inc/directoryOperation.h"
#include "../inc/passwordConfirm.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>

static int interactiveWithClient(int clientFileDescribe);
static void* threadFunction(void *p);

/*
 * @brief 对recv函数的返回值进行check
 * @param in recvReturnValue recv函数的返回值
 * @return 返回0表示recv正常读取，-1表示读取异常，-2表示断开连接
 */
static inline int recvReturnCheck(int recvReturnValue) {
    if (0 == recvReturnValue) {
        printk("client disconnect!\n");
        return -2;
    }
    if (-1 == recvReturnValue) {
        return -1;
    }
    return 0;
}

/*
 * @brief 获取一帧数据，即一个结构体protocol_t存储的数据
 * @param in clientFileDescribe 客户端文件描述符
 * @param out pProtocol 用于返回获取到的一帧数据
 * @return 返回0表示获取成功，-1表示获取失败，-2表示断开连接
 */
int getOneFrame(int clientFileDescribe, protocol_t *pProtocol) {
    int type = 0;
    int size = 0;
    // 获取数据类型
    int ret = recv(clientFileDescribe, &type, sizeof type, MSG_WAITALL);
    // printk("get command type 0x%4X\n", type);
    if (0 != (ret = recvReturnCheck(ret))) {
        return ret;
    }
    // 获取数据长度
    ret = recv(clientFileDescribe, &size, sizeof size, MSG_WAITALL);
    if (0 != (ret = recvReturnCheck(ret))) {
        return ret;
    }
    pProtocol->type = type;
    pProtocol->size = size;
    // printk("command size = %d\n", size);
    if (0 == size) return 0;
    // 获取数据部分
    ret = recv(clientFileDescribe, pProtocol->data, pProtocol->size, MSG_WAITALL);
    if (0 != recvReturnCheck(ret)) {
        return -1;
    }
    // printk("get command: %s\n", pProtocol->data);
    pProtocol->data[size] = 0;
    return 0;
}

/*
 * @brief 往客户端发送显示消息
 * @param clientFileDescribe in 客户端文件描述符
 * @param message in 要发送的消息
 * @return 返回0表示发送成功
 */
int sendMessage(int clientFileDescribe, const char *message) {
    int messageLength = strlen(message);
    protocol_t *pProtocol = (protocol_t *)calloc(sizeof *pProtocol + messageLength, sizeof(char));
    pProtocol->type = MESSAGE;
    pProtocol->size = messageLength;
    memcpy(pProtocol->data, message, messageLength);
    send(clientFileDescribe, pProtocol, getProtocolSize(pProtocol), 0);
    free(pProtocol);
    return 0;
}

/*
 * @brief 等待客户端输入用户名密码并验证
 * @param clientFileDescribe in 客户端文件描述符
 * @return 0表示登录成功，-1表示接收失败，-2表示断开连接
 */
int waitClientSignIn(int clientFileDescribe) {
    protocol_t *pProtocol = (protocol_t *)malloc(sizeof *pProtocol + MAX_DATA_SIZE + 1);
    int ret = 0;
    while (1) {
        // 获取用户输入的用户名和密码
        ret = getOneFrame(clientFileDescribe, pProtocol);
        if (0 != ret) {
            break;
        }

        // 用户名和密码用空格隔开，所以找到第一个空格，之后就是密码
        char *userName = pProtocol->data;
        char *password = pProtocol->data;
        while (' ' != *password && 0 != *password) password++;
        *password = 0;
        password++;
        pProtocol->data[pProtocol->size] = 0;

        // 确认用户名和密码是否正确
        if (0 == passwordConfirm(userName, password)) {
            pProtocol->type = PASSWD_RIGHT;
            pProtocol->size = 0;
            send(clientFileDescribe, pProtocol, getProtocolSize(pProtocol), 0);
            break;
        }
        pProtocol->type = PASSWD_ERROR;
        pProtocol->size = 0;
        send(clientFileDescribe, pProtocol, getProtocolSize(pProtocol), 0);
    }
    free(pProtocol);
    return ret;
}

/*
 * @brief 与客户端交互主函数
 * @param in clientFileDescribe 客户端的文件描述符
 * @return 返回0表示正常执行，-1表示未通过用户验证
 */
static int interactiveWithClient(int clientFileDescribe) {
    protocol_t *pProtocol = (protocol_t *)malloc(sizeof *pProtocol + MAX_DATA_SIZE + 1);
    int ret = 0;
    char *message = (char *)malloc(MAX_DATA_SIZE);
    ret = waitClientSignIn(clientFileDescribe);
    if (0 != ret) {
        return -1;
    }
    while (1) {
        ret = getOneFrame(clientFileDescribe, pProtocol);
        if (0 != ret) {
            break;
        }
        message[0] = 0;
        switch (pProtocol->type) {
            case CD:
                changeDirectory(pProtocol->data);
                break;
            case LS:
                memset(message, 0, MAX_DATA_SIZE);
                listFile(".", message, MAX_DATA_SIZE - 1);
                break;
            case PWD:
                memset(message, 0, MAX_DATA_SIZE);
                printWorkingDirectory(message, MAX_DATA_SIZE - 1);
                break;
            case RM:
                remove(pProtocol->data);
                break;
            case MKDIR:
                makeDirectory(pProtocol->data);
                break;
            case PUTS:
                getFile(clientFileDescribe, pProtocol->data);
                break;
            case GETS:
                putFile(clientFileDescribe, pProtocol->data);
                break;
            default: break;
        }
        if (0 != message[0]) {
            sendMessage(clientFileDescribe, message);
            printk("%s\n", message);
        }
    }
    free(message);
    free(pProtocol);
    return 0;
}

static void* threadFunction(void *p) {
    threadPool_t *pThreadPool = (threadPool_t *)p;
    printk("subThread create success!\n");
    while (1) {
        pthread_mutex_lock(&pThreadPool->mutex);
        if (0 == pThreadPool->pThreadQueue->size) {
            // 等待队列中有就绪的文件描述符
            pthread_cond_wait(&pThreadPool->cond, &pThreadPool->mutex);
        }
        int clientFileDescribe = 0;
        int ret = queuePop(pThreadPool->pThreadQueue, &clientFileDescribe);
        pthread_mutex_unlock(&pThreadPool->mutex);
        if (-1 != ret) {
            printk("connect success!\n");
            // 连接成功后，与客户端进行交互
            interactiveWithClient(clientFileDescribe);
            close(clientFileDescribe);
        }
    }
    pthread_exit(NULL);
}

/*
 * @brief 初始化线程池
 * @param in threadCount 设置线程池中线程的个数
 * @param in ppThreadPool 直线线程池结构体的二级指针
 * @return 返回0表示初始化成功，其它值未定义
 */
int threadPoolInit(int threadCount, threadPool_t **ppThreadPool) {
    *ppThreadPool = (threadPool_t *)malloc(sizeof(threadPool_t));
    (*ppThreadPool)->threadCount = threadCount;
    (*ppThreadPool)->pThreadIds = (pthread_t *)malloc(threadCount * sizeof(pthread_t));
    queueInit(&(*ppThreadPool)->pThreadQueue);

    pthread_mutex_init(&(*ppThreadPool)->mutex, NULL);
    pthread_cond_init(&(*ppThreadPool)->cond, NULL);
    return 0;
}

/*
 * @brief 启动线程池
 * @param in pThreadPool 线程池结构体指针
 * @return 返会0表示启动成功，其它值未定义
 */
int threadPoolStart(threadPool_t *pThreadPool) {
    for (int i = 0; i< pThreadPool->threadCount; i++) {
        pthread_create(pThreadPool->pThreadIds + i, NULL, threadFunction, pThreadPool);
    }
    printk("start threadPool success");
    return 0;
}